﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using EzDesk.Core.Model;
using EzDesk.Utility;
using EzDesk.Utility.EzML;
using EzDesk.Utility.Json;
using EzDesk.Core.Interface;
using EzDesk.Utility.Log;
using EzDesk.Utility.Web.Extensions;

namespace EzDesk.Core.ServiceImpl.Services
{
    public partial class CoreService : ICoreService
    {
        #region [ Send Feeds Methods ]


        private Guid innerSendFeed(FeedTemplate template, string feedData, string memberString, Guid? senderId, string ignoreUsers, bool isAvailable)
        {
            Guid templateId = template.Id;
            int feedType = template.DefaultType;

            // 验证FeedData和模板的匹配
            try
            {
                validateFeedData(template, feedData);
            }
            catch (Exception ex)
            {
                throw new Exception("FeedData Error!", ex);
            }


            //处理ez标记
            feedData = replaceEzTarget(feedData);
            // 处理掉feedData中的html, 只留下空格
            feedData = feedData.RemoveHtml(false);
            //恢复ez标记
            feedData = recoveryEzTarget(feedData);

            Guid feedId = Guid.NewGuid();
            DateTime now = DateTime.Now;

            // 创建 Feed
            Feed feed = new Feed()
            {
                Id = feedId,
                TemplateId = templateId,
                CreateOn = now,
                UpdateOn = now,
                LastChangeOn = now,
                ExpireOn = now.AddDays(2), /* todo: update 过期时间 */
                FeedData = feedData,
                IsRead = false,
                IsAvailable = isAvailable,
                OwnUserId = senderId,
                FeedType = feedType,
                Icon = template.TemplateIconName,
            };
            _feedRepository.Add(feed, false);
            LogEx.TRACE("Core - SendFeed", "新Feed写入数据库成功 " + feed.Id);

            // 把Feed发送给的人
            IList<Guid> usersIdsToSend = getAllUserIdsByMemberString(memberString);

            var allUserIds = getAllUserIdsByMemberString(memberString);
            var ignoreUserIds = getAllUserIdsByMemberString(ignoreUsers);

            allUserIds
                .Where(p => !ignoreUserIds.Contains(p))
                .Foreach(userId =>
                    _feedRepository.AddUserToFeed(userId, feedId, feedType, null, now)
                    );
            _feedRepository.SaveChanges();

            return feedId;
        }

        Guid ICoreService.SendFeed(
            string templateName, string feedData, string memberString, DateTime expireOn,
            Guid? senderId, string ignoreUsers, bool isAvailable
            )
        {
            // 消息模板
            var template = _feedTemplateRepository.GetAll().Where(p => p.TemplateName == templateName).SingleOrDefault();
            if (template == null)
            {
                throw new Exception("Feed Template not Found!");
            }

            return innerSendFeed(template, feedData, memberString, senderId, ignoreUsers, isAvailable);
        }




        private void validateFeedData(FeedTemplate template, string feedData)
        {
            EzMLHelper.MergeDataToEzML(template.Title, feedData);
            EzMLHelper.MergeDataToEzML(template.Body, feedData);
        }


        void ICoreService.UpdatePersonalFeedData(Guid feedId, string memberstring, string feedDataEx)
        {
            var userIds = self.GetAllUserIdsByMemberString(memberstring);
            foreach (var userId in userIds)
            {
                // 合并FeedData
                string originalData = _feedRepository.GetPersonalFeedData(userId, feedId);
                var feedDataToUpdate = merageExtraFeedData(originalData, feedDataEx);
                if (feedDataToUpdate != originalData)
                {
                    _feedRepository.UpdatePersonalFeedData(userId, feedId, feedDataToUpdate);
                    // 更新数据后，设为未读
                    setFeedIsRead(feedId, userId.ToString(), false);
                }
            }
            _feedRepository.SaveChanges();
        }

        void ICoreService.UpdatePersonalFeedType(Guid feedId, string memberstring, string feedTypeStr)
        {
            var feedType = FeedTypeDictionary.StringToIntType(feedTypeStr);
            var userIds = self.GetAllUserIdsByMemberString(memberstring);
            foreach (var userId in userIds)
            {
                _feedRepository.UpdatePersonalFeedType(userId, feedId, feedType);
                // 更新数据后，设为未读
                setFeedIsRead(feedId, userId.ToString(), false);
            }
            _feedRepository.SaveChanges();
        }

        #endregion

        #region [ Update Feeds Methods]

        void ICoreService.UpdateFeedData(Guid feedId, string newFeedData)
        {
            updateFeedData(
                convertSingleIdToIdCollection(feedId),
                newFeedData
                );
        }

        void ICoreService.UpdateFeedData(string feedIdStrs, string newFeedData)
        {
            updateFeedData(
                convertIdStrsToIdCollection(feedIdStrs),
                newFeedData
                );
        }

        void ICoreService.UpdateFeedStatus(IList<FeedStatus> feedStatus, string memberString)
        {
            foreach (var feed in feedStatus)
            {
                try
                {
                    if (feed.IsRead.HasValue) setFeedIsRead(feed.FeedId, memberString, feed.IsRead.Value);
                    if (feed.IsAvailable.HasValue) setFeedIsAvailable(feed.FeedId, memberString, feed.IsAvailable.Value);
                    if (feed.IsMark.HasValue) setFeedIsMark(feed.FeedId, memberString, feed.IsMark.Value);
                }
                catch (Exception ex)
                {
                    LogEx.ERROR("Core - FeedUpdate", "更新Feed时失败", ex);
                    continue;
                }
            }
        }

        private string merageNewFeedData(string originalFeedData, string newFeedData)
        {
            IDictionary<string, string> orignalData = JsonHelper.JsonToDictionary(originalFeedData);
            IDictionary<string, string> newData = JsonHelper.JsonToDictionary(newFeedData);
            // 更新 原有字段信息
            foreach (var item in newData)
            {
                if (orignalData.ContainsKey(item.Key))
                {
                    orignalData[item.Key] = item.Value;
                }
                else
                {
                    //todo: error
                    throw new Exception("Feed Data Error! " + item.Key + " not found!");
                }
            }
            return JsonHelper.ConvertToJson(orignalData);
        }

        private string merageExtraFeedData(string originalFeedData, string newFeedData)
        {
            IDictionary<string, string> orignalData = JsonHelper.JsonToDictionary(originalFeedData);
            IDictionary<string, string> newData = JsonHelper.JsonToDictionary(newFeedData);

            foreach (var item in newData)
            {
                if (orignalData.ContainsKey(item.Key))
                {
                    orignalData[item.Key] = item.Value;
                }
                else
                {
                    orignalData.Add(item.Key, item.Value);
                }
            }
            return JsonHelper.ConvertToJson(orignalData);
        }
        #endregion

        #region [ Delete Methods ]
        void ICoreService.DeleteFeed(Guid feedId, int type)
        {
            Feed feed = _feedRepository.GetById(feedId);
            if (feed != null)
            {
                _feedRepository.Delete(feed.Id);
            }
            else
            {
                throw new Exception("Feed Not Found");
            }
            return;
        }
        #endregion


        #region [ Get Feeds ]

        IList<Feed> ICoreService.GetFeeds(string feedTypeStr, Guid userId, DateTime from, DateTime to, int maxCount, bool? isRead, bool? isAvaiable, bool? isMark)
        {
            int feedType = FeedTypeDictionary.StringToIntType(feedTypeStr);

            if (feedType < -1)
            {
                throw new Exception("Feed Type Error!");
            }

            IQueryable<Feed> feeds = _feedRepository
                .GetByUser(userId, isRead, isAvaiable, isMark)
                .Where(p => p.FeedType == feedType);

            if (from > DateTime.MinValue)
            {
                feeds = feeds.Where(p => p.UpdateOn > from || p.LastChangeOn > from);
            }
            if (to < DateTime.MaxValue)
            {
                feeds = feeds.Where(p => p.UpdateOn < to || p.LastChangeOn < to);
            }

            feeds = feeds.OrderByDescending(p => p.UpdateOn);

            // 限量..
            feeds = feeds.Take(maxCount);

            return feeds.ToList();
        }

        IList<Feed> ICoreService.GetFeeds(string feedTypeStr, Guid userId, int startIndex, int count, bool? isRead, bool? isAvaiable, bool? isMark)
        {
            int feedType = FeedTypeDictionary.StringToIntType(feedTypeStr);

            if (feedType < -1)
            {
                throw new Exception("Feed Type Error!");
            }

            IQueryable<Feed> feeds = _feedRepository
                .GetByUser(userId, isRead, isAvaiable, isMark)
                .Where(p => p.FeedType == feedType)
                .OrderByDescending(p => p.UpdateOn)
                .Skip(startIndex)
                .Take(count);

            return feeds.ToList();

        }

        #endregion

        string ICoreService.GetPersonalFeedData(Guid userId, Guid feedId)
        {
            return _feedRepository.GetPersonalFeedData(userId, feedId);
        }


        #region [ Feed Records ]
        public void SetFeedRecord(Guid identity, string templateName, string memberstring, Guid feedId, string ignoreUsers)
        {
            var allUserIds = getAllUserIdsByMemberString(memberstring);
            var ignoreUserIds = getAllUserIdsByMemberString(ignoreUsers);

            allUserIds
                .Where(p => !ignoreUserIds.Contains(p))
                .Foreach(userId =>
                    _feedRepository.AddFeedRecord(identity, templateName, userId, feedId)
                    );
            _feedRepository.SaveChanges();


        }

        public string GetFeedRecord(Guid identity, string templateName, string userIds)
        {
            var allUserIds = getAllUserIdsByMemberString(userIds);
            IList<Guid> feedIdList = new List<Guid>();

            if (allUserIds.Count == 0)
            {
                feedIdList = _feedRepository.GetFeedRecord(identity, templateName, null);
            }
            else
            {
                IEnumerable<Guid> feedList = feedIdList;
                allUserIds.Foreach(
                    userId =>
                    {
                        feedList = feedList.Union(_feedRepository.GetFeedRecord(identity, templateName, userId));
                    });
                feedIdList = feedList.ToList();
            }
            return feedIdList.Join(",");

        }



        #endregion

        #region [ private methods - Set Feed Properties Methods]

        void updateFeedData(Guid[] feedIds, string newFeedData)
        {
            foreach (var feedId in feedIds)
            {
                Feed feed = _feedRepository.GetById(feedId);
                FeedTemplate feedTemplate = _feedTemplateRepository.GetById(feed.TemplateId);

                    string newFeedDataToUpdate = merageNewFeedData(feed.FeedData, newFeedData);
                    validateFeedData(feedTemplate, newFeedDataToUpdate);
                    feed.FeedData = newFeedDataToUpdate;
                    feed.UpdateOn = DateTime.Now;
                    // 更新了数据 设为未读
                    feed.IsRead = false;
                    _feedRepository.Update(feed, false);
                    continue;
            }
            _feedRepository.SaveChanges();
            return;
        }

        void setFeedIsRead(Guid feedId, string memberString, bool isRead)
        {
            setFeedIsRead(
                convertSingleIdToIdCollection(feedId),
                memberString,
                isRead);
        }
        void setFeedIsRead(string feedIdStrs, string memberString, bool isRead)
        {
            setFeedIsRead(
                convertIdStrsToIdCollection(feedIdStrs),
                memberString,
                isRead
                );
        }

        void setFeedIsRead(Guid[] feedIds, string memberString, bool isRead)
        {
            var userIds = self.GetAllUserIdsByMemberString(memberString);
            foreach (var feedId in feedIds)
            {
                foreach (var userId in userIds)
                {
                    _feedRepository.SetIsRead(feedId, userId, isRead, false);
                }
            }
            _feedRepository.SaveChanges();
        }


        void setFeedIsAvailable(Guid feedId, string memberString, bool isAvailable)
        {
            setFeedIsAvailable(
                convertSingleIdToIdCollection(feedId),
                memberString,
                isAvailable
                );
        }


        void setFeedIsAvailable(string feedIdStrs, string memberString, bool isAvailable)
        {
            setFeedIsAvailable(
                convertIdStrsToIdCollection(feedIdStrs),
                memberString,
                isAvailable
                );
        }

        void setFeedIsAvailable(Guid[] feedIds, string memberString, bool isAvailable)
        {
            var userIds = self.GetAllUserIdsByMemberString(memberString);
            foreach (var feedId in feedIds)
            {
                foreach (var userId in userIds)
                {
                    _feedRepository.SetIsAvailable(feedId, userId, isAvailable, false);
                }
            }
            _feedRepository.SaveChanges();
        }

        void setFeedIsMark(string feedIdStrs, string memberString, bool isMark)
        {
            setFeedIsMark(
                convertIdStrsToIdCollection(feedIdStrs),
                memberString,
                isMark
                );
        }

        void setFeedIsMark(Guid feedId, string memberString, bool isMark)
        {
            setFeedIsMark(
                convertSingleIdToIdCollection(feedId),
                memberString,
                isMark);
        }

        void setFeedIsMark(Guid[] feedIds, string memberString, bool isMark)
        {
            var userIds = self.GetAllUserIdsByMemberString(memberString);
            foreach (var feedId in feedIds)
            {
                foreach (var userId in userIds)
                {
                    _feedRepository.SetIsMark(feedId, userId, isMark, false);
                }
            }
            _feedRepository.SaveChanges();
        }

        private string replaceEzTarget(string str)
        {
            var strReplaced = Regex.Replace(str, @"</ez:(?<content>.*?)>", @"[/ez:${content}]", RegexOptions.IgnoreCase);
            strReplaced = Regex.Replace(strReplaced, @"<ez:(?<content>.*?)>", @"[ez:${content}]", RegexOptions.IgnoreCase);

            return strReplaced;
        }

        private string recoveryEzTarget(string str)
        {
            var strReplaced = Regex.Replace(str, @"\[/ez:(?<content>.*?)\]", @"</ez:${content}>", RegexOptions.IgnoreCase);
            strReplaced = Regex.Replace(strReplaced, @"\[ez:(?<content>.*?)\]", @"<ez:${content}>", RegexOptions.IgnoreCase);

            return strReplaced;
        }

        #endregion

    }
}
